#include "glare.core.DrawContext.h"
#include "glare.core.ColourValue.h"

#include "Demo.Scene.h"
#include "glare.core.ShaderFactory.h"

#if GLARE_PLATFORM == GLARE_PLATFORM_WIN32
#	include "glare.d3d11.Driver.h"
#endif

#include "glare.gles2.Driver.h"
#include "glare.core.Shader.h"
#include "glare.core.ShaderInputDeclaration.h"
#include "glare.core.GpuBufferDescription.h"
#include "glare.core.GpuResourceFactory.h"
#include "Glare.Core.GpuBuffer.h"

using namespace Glare;

class HelloTriangle : public DemoScene
{
public : virtual void Initialize(Driver* driver, PlatformWindow* platform_window) 
		 {
			 _driver = driver;

			 try
			 {
				 _render_window = driver->CreateRenderWindow(platform_window);
				 _render_window->Initialize();
				 _render_window->SetPresentSyncInterval(1);

				 _immediate_context = _driver->GetImmediateDrawContext();

				 _viewport.Left = 0;
				 _viewport.Top = 0;
				 _viewport.Width = (float)_render_window->GetCanvasWidth();
				 _viewport.Height = (float)_render_window->GetCanvasHeight();
				 _viewport.MinDepth = 0;
				 _viewport.MaxDepth = 1;

				 // get the Shader / InputDeclaration factories
				 ShaderFactory* shader_factory = driver->GetShaderFactory();
				 _vertex_shader = shader_factory->CreateVertexShader();
				 _pixel_shader = shader_factory->CreatePixelShader();

				 string errors;
				 // load the vertex & pixel shader
#if GLARE_PLATFORM == GLARE_PLATFORM_WIN32
				 if (driver->GetName() == D3D11Driver::Name)
				 {
					 _vertex_shader->Compile("float4 main(float4 position: POSITION) : SV_POSITION { return position; }", &errors);

					 if (errors.length() > 0)
						 driver->WriteLog("error while compiling vertex shader: " + errors, LMT_Error);

					 _pixel_shader->Compile("float4 main(float4 Pos : SV_POSITION) : SV_Target { return float4(0,1,0,1); }", &errors);

					 if (errors.length() > 0)
						 driver->WriteLog("error while compiling pixel shader: " + errors, LMT_Error);
				 }
#endif
				 if (driver->GetName() == GLES2Driver::Name)
				 {
					 _vertex_shader->Compile("attribute vec4 POSITION; void main(void) { gl_Position = POSITION; }", &errors);

					 if (errors.length() > 0)
						 driver->WriteLog("error while compiling vertex shader: " + errors, LMT_Error);

					 _pixel_shader->Compile("void main(void) { gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); }", &errors);

					 if (errors.length() > 0)
						 driver->WriteLog("error while compiling pixel shader: " + errors, LMT_Error);
				 }

				 // create an input declaration for the vertex shader
				 _input_decl = shader_factory->CreateInputDeclaration();

				 // add the elements to the input declaration
				 ShaderInputElement position_elem;
				 position_elem.Format = PF_R32G32B32_FLOAT;
				 position_elem.SemanticName = "POSITION";
				 _input_decl->InputElements.push_back(position_elem);

				 // build the input declaration for the vertex shader
				 _input_decl->Build(_vertex_shader);

				 GpuResourceFactory* resource_factory = driver->GetGpuResourceFactory();

				 GpuBufferDescription desc;
				 desc.BindFlags = GpuResourceBind::VertexBuffer;
				 desc.ElementSize = sizeof(Vector3);
				 desc.ElementCount = 3;

				 // create the vertex buffer
				 _vertex_buffer = resource_factory->CreateBuffer(desc);

				 // create a binding for the vertex buffer
				 _vb_binding.Buffer = _vertex_buffer;
				 _vb_binding.Stride = sizeof(Vector3);
				 _vb_binding.Offset = 0;

				 Box dest_box;
				 dest_box.right = sizeof(Vector3) * 3;

				 Vector3 vertices[3] = 
				 {
					 Vector3(-1, -1, 0),
					 Vector3(0, 1, 0),
					 Vector3(1, -1, 0)
				 };

				 // copy the data to the vertex buffer
				 _immediate_context->UpdateGpuResource(_vertex_buffer, 0, &dest_box, vertices, sizeof(Vector3), 0);
			 }
			 catch (Exception e)
			 {
				 _driver->WriteLog(e.what(), LMT_Error);
			 }
		 }

public : virtual void DrawFrame() 
		 {
			 // set the RenderTarget, the Viewport and perform a clearing operation
			 _immediate_context->OM_SetRenderTarget(_render_window, NULL);
			 _immediate_context->RS_SetViewport(_viewport);
			 _immediate_context->ClearRenderTarget(_render_window, ColourValue::Red);

			 // activate the vertex buffer
			 _immediate_context->IA_SetVertexBuffer(_vb_binding);

			 // set the vertex & pixel shader
			 _immediate_context->VS_SetVertexShader(_vertex_shader);
			 _immediate_context->PS_SetPixelShader(_pixel_shader);

			 // set the primitive type & input declaration for the draw call
			 _immediate_context->IA_SetPrimitiveType(GpuPrimitive::TriangleList);
			 _immediate_context->IA_SetInputDeclaration(_input_decl);

			 // submit the draw call
			 _immediate_context->Draw(3);

			 // swap the GPU buffers
			 _render_window->Present();
		 }

public : virtual void Destroy() 
		 {
			 delete _vertex_buffer;
			 _vertex_buffer = NULL;

			 delete _input_decl;
			 _input_decl = NULL;

			 delete _pixel_shader;
			 _pixel_shader = NULL;

			 delete _vertex_shader;
			 _vertex_shader = NULL;

			 _driver->DestroyRenderWindow(_render_window);
			 _render_window = NULL;
		 }

private : Driver* _driver;
private : RenderWindow* _render_window;
private : DrawContext* _immediate_context;
private : Viewport _viewport;

private : VertexShader* _vertex_shader;
private : PixelShader* _pixel_shader;
private : ShaderInputDeclaration* _input_decl;
private : GpuBuffer* _vertex_buffer;
private : GpuVertexBufferBinding _vb_binding;
};
